//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//------------------------------------------------------------------------------
//
//  File:  flash.c
//
//  Flash routines for the Marvell Littleton bootloader.
//
#include <windows.h>
#include <oal_memory.h>
#include "loader.h"
#include <nandfmd.h>
#include <flash_cfg.h>

// Defines.
//
#ifndef MIN
#define MIN(x, y)   (((x) < (y)) ? (x) : (y))
#endif

#define FLASH_DEVICE_ID_L18_MIN    0x880C
#define FLASH_DEVICE_ID_L18_MAX    0x880F
#define FLASH_DEVICE_ID_L30_MIN    0x8812
#define FLASH_DEVICE_ID_L30_MAX    0x8815
#define FLASH_DEVICE_ID_K3_MIN     0x8801
#define FLASH_DEVICE_ID_K3_MAX     0x8803
#define FLASH_DEVICE_ID_K18_MIN    0x8805
#define FLASH_DEVICE_ID_K18_MAX    0x8807
#define FLASH_DEVICE_ID_J3_MIN     0x0016
#define FLASH_DEVICE_ID_J3_MAX     0x0018

#define NAND_ATTEMPT_NUMBER 3
// defines needed for FMD writes
#define SECTOR_WRITE_IN_PROGRESS   0x0002
#define SECTOR_WRITE_COMPLETED     0x0004


// Type definitions.
//


typedef enum _FLASH_BLOCK_ARRAY_TYPE_
{
    CONSTANT_SIZE,
    BOTTOM_PARAMETER,
    TOP_PARAMETER
} FLASH_BLOCK_ARRAY_TYPE, *PFLASH_BLOC_ARRAY_TYPE;

typedef enum _FLASH_DEVICE_TYPE_
{
    NONE,
    L18, 
    L30,
    K3,
    K18,
    J3,
    SAMSUNG_ZYLONYTE
} FLASH_DEVICE_TYPE, *PFLASH_DEVICE_TYPE;

// Local variables.
//
static FLASH_BLOCK_ARRAY_TYPE g_BlockArrayType  = CONSTANT_SIZE;
static FLASH_DEVICE_TYPE      g_FlashDeviceType = NONE;

static UINT32 FLASH_SIZE    = (16777216 * 2);   // Paired 16MB parts.
static UINT32 ERASE_BLOCKS  = 128;              // Total number of blocks (hiding parameter blocks).
static UINT32 L3_BLOCK_SIZE = 65536;            // 64KB per block on L3 (blocks 0 - 3).

static UINT8 tempBuffer[LB_BYTES_PER_PAGE]; //SB:512 bytes/page  LB:2048 bytes/page

// Global variables.
//
extern IMAGE_TYPE g_ImageType;
extern IMAGE_TYPE g_ImageType;
extern EBOOT_CONTEXT g_EBootContext;
LPBYTE g_lastAddressMapped;

static BOOL ReadBlock(BLOCK_ID block, UINT8 *pDataBuffer, FlashInfo * pFlashInfo);
static UINT8 *WriteBlock(BLOCK_ID block, UINT8 *pDataBuffer, FlashInfo * pFlashInfo, BOOL fSectorInfo);
static BLOCK_ID GetNextReservedBlock(pTableAddress);
static UINT32 GetRelocTableOffset(PLATFORM_MODE_T mode, UINT32 blockSize);
static BOOL UpdateBadBlockTable(Reloc_T * pTableAddress, Rel_T * pTableEntry); 
static BOOL StartFlashWriteBINDIO(DWORD dwStartAddr, DWORD dwLength);
static BOOL ContinueFlashWriteBINDIO(PEBOOT_CONTEXT pEBootContext);
static void DumpBuffer(UINT8 *pImageBufferRam, UINT32 size);


//------------------------------------------------------------------------------
//
//  Function:  OEMIsFlashAddr
//
// Checks the address provided and determines if it's a flash address or not.
//
BOOL OEMIsFlashAddr(DWORD dwAddr)
{

    DWORD dwVirtFlashStart = (DWORD) OALPAtoVA(LITTLETON_BASE_PA_BOOT_FLASH, FALSE);
    //EdbgOutputDebugString("OEMIsFlashAddr phy (0x%x) => virt (0x%x), dwAddr (0x%x)\r\n", Littleton_BASE_PA_BOOT_FLASH, dwVirtFlashStart, dwAddr);  
    
    if ((dwAddr >= dwVirtFlashStart) && (dwAddr <= (dwVirtFlashStart + LITTLETON_SIZE_BOOT_FLASH)))
    {
        return(TRUE);
    }

    // Support quickly debug NK partition
    if ((dwAddr >= IMAGE_BOOT_DEBUGNK_UA) && (dwAddr <= (IMAGE_BOOT_DEBUGNK_UA + 0x600000)))
    {
        return TRUE;
    }

    if (g_ImageType == IMAGE_TYPE_FLASHIMAGE)
    {
        //EdbgOutputDebugString("OEMIsFlashAddr: FlashImage\r\n");
        return TRUE;
    }

    EdbgOutputDebugString("OEMIsFlashAddr return FALSE\r\n");
    return(FALSE);               // for now, using NAND, we do not expect anything other than a RAM-based address.
}

//------------------------------------------------------------------------------
//
//  Function:  OEMMapMemAddr
//
// Maps a flash address to a RAM address (used when temporarily caching a
// flash-destined download image to RAM).
//
LPBYTE OEMMapMemAddr (DWORD dwImageStart, DWORD dwAddr)
{
    if (OEMIsFlashAddr(dwAddr))
    {
        if (g_ImageType == IMAGE_TYPE_FLASHIMAGE)
        {
            g_EBootContext.dwOffsetAddr = g_EBootContext.dwBaseAddr + (dwAddr-dwImageStart);
            //EdbgOutputDebugString("OffsetAddr 0x%x, BaseAddr 0x%x\r\n", g_EBootContext.dwOffsetAddr, g_EBootContext.dwBaseAddr);      
        }
        if (g_ImageType == IMAGE_TYPE_DEBUGNK )
        {
            //EdbgOutputDebugString("OEMMapMemAddr Start 0x%X, Addr 0x%X\r\n", dwImageStart,dwAddr);
            dwAddr = dwAddr-(IMAGE_BOOT_DEBUGNK_UA&0xFF000000) + 0x80000000;
            //EdbgOutputDebugString("OEMMapMemAddr 0x%X\r\n", dwAddr);
            g_lastAddressMapped = ((LPBYTE) (dwAddr));
            return ((LPBYTE) (dwAddr));
        }

        // The image being downloaded is a flash image - temporarily
        // cache the image in RAM until it's completely downloaded.
        //        
        dwAddr += DOWNLOAD_RAM_BUFFER_START;
        dwAddr -= dwImageStart;
    }
    //EdbgOutputDebugString("OEMMapMemAddr dwImageStart:0x%x, dwAddr mapped to:0x%x\r\n", dwImageStart, dwAddr);

    g_lastAddressMapped = ((LPBYTE) (dwAddr));
    return((LPBYTE) (dwAddr));
}

//------------------------------------------------------------------------------
//
//  Function:  OEMStartEraseFlash
//
// Begins the flash erase procedure.
//
BOOL OEMStartEraseFlash (DWORD dwStartAddr, DWORD dwLength)
{   
    if (g_ImageType == IMAGE_TYPE_FLASHIMAGE) 
        StartFlashWriteBINDIO(dwStartAddr, dwLength);
    
    return(TRUE);
}


//------------------------------------------------------------------------------
//
//  Function:  main
//
// Continues the flash erase procedure (erases are incrementally done during
// download to speed up the overall flash operation from the user's perspective).
//
void OEMContinueEraseFlash(void)
{
    if (g_ImageType == IMAGE_TYPE_FLASHIMAGE)
        ContinueFlashWriteBINDIO(&g_EBootContext);

    return;
}


//------------------------------------------------------------------------------
//
//  Function:  OEMFinishEraseFlash
//
// Finished the flash erase procedure.
//
BOOL OEMFinishEraseFlash(void)
{
    if (g_ImageType == IMAGE_TYPE_FLASHIMAGE)
        UpdateImageAddr((BLOCK_ID)g_EBootContext.dwStartBlock, (UINT16)g_EBootContext.dwNumBlocksWritten);
    return(TRUE);
}


BOOL OEMWriteFlash(DWORD dwImageStart, DWORD dwImageLength)
{
    BOOL  retVal = FALSE;
    UINT8 *pDataBuffer = (UINT8 *) OEMMapMemAddr(dwImageStart, dwImageStart);
    UINT32 startBlock;
    //ROMHDR *pTOC;
    //UINT32 *pInfo;
    FlashInfo flashInfo;

    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: OEMWriteFlash: FMD_GetInfo call failed!\r\n");
        return FALSE;
    }

    if( g_ImageType == IMAGE_TYPE_DEBUGNK )
    {
        return TRUE;
    }

    EdbgOutputDebugString("OEMWriteFlash addr: 0x%x, length: 0x%x\r\n", dwImageStart, dwImageLength);
//  dumpData((unsigned char *)pDataBuffer, 2000, 0);
    if (g_ImageType == IMAGE_TYPE_BOOTLOADER)
    {
        if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
        {
            startBlock = LB_FLASH_BLOCK_EBOOT_START;
            dwImageLength = LB_FLASH_BLOCK_EBOOT_LENGTH*LB_BYTES_PER_PAGE*LB_PAGES_PER_BLOCK;
        }
        else
        {
            startBlock = FLASH_BLOCK_EBOOT_START;
            dwImageLength = FLASH_BLOCK_EBOOT_LENGTH*SB_BYTES_PER_PAGE*SB_PAGES_PER_BLOCK;
        }

        EdbgOutputDebugString("g_ImageType = IMAGE_TYPE_BOOTLOADER startBlock = %d dwImageLength = %d\r\n", startBlock, dwImageLength);
        retVal = FlashWriteNAND(startBlock, pDataBuffer, dwImageLength);    
    }
    else if (g_ImageType == IMAGE_TYPE_IPL)
    {
        if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
        {
            startBlock = LB_FLASH_BLOCK_IPL_START;
            dwImageLength = LB_FLASH_BLOCK_IPL_LENGTH*LB_BYTES_PER_PAGE*LB_PAGES_PER_BLOCK;
        }
        else
        {
            startBlock = FLASH_BLOCK_IPL_START;
            dwImageLength = FLASH_BLOCK_IPL_LENGTH*SB_BYTES_PER_PAGE*SB_PAGES_PER_BLOCK;
        }

        EdbgOutputDebugString("g_ImageType = IMAGE_TYPE_IPL startBlock = %d dwImageLength = %d\r\n", startBlock, dwImageLength);
        retVal = FlashWriteNAND(startBlock, pDataBuffer, dwImageLength);    
    }
    else
    {
        retVal = TRUE; 
#ifdef OSV_WINCENET
        if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
            startBlock = LB_FLASH_BLOCK_OS_START;
        else
            startBlock = FLASH_BLOCK_OS_START;
        retVal = FlashWriteNAND(startBlock, pDataBuffer, dwImageLength);    
#endif
    }

    // If we just wrote the bootloader to flash, halt and require the system to be rebooted.
    if ((g_ImageType == IMAGE_TYPE_BOOTLOADER) || (g_ImageType == IMAGE_TYPE_IPL))
    {
        EdbgOutputDebugString("INFO: bootloader image stored successfully.\r\n");
        EdbgOutputDebugString("Reboot the system .....  ");
        //Reboot();
        SpinForever();
    }

    return(retVal);

}



BOOL FlashInit(UINT32 FlashStart, UINT32 FlashLength)
{
    PVOID hFMD = NULL;
        hFMD = FMD_Init(FMD_CALLER_EBOOT, NULL, NULL);
    return((hFMD != NULL) ? TRUE : FALSE);
}

BOOL FlashReadSector(SECTOR_ADDR sector, UINT8 *pTmp, SectorInfo *pSectorInfo)
{
    BOOL rc;
    UINT32 retry = NAND_ATTEMPT_NUMBER;
    do {
        rc=FMD_ReadSector(sector, pTmp, pSectorInfo, 1);
    }while (!rc && --retry > 0);
    return rc;
}

BOOL FlashWriteSector(SECTOR_ADDR sector, VOID *pBuffer, SectorInfo *pInfo)
{
    BOOL rc;
    UINT32 retry = NAND_ATTEMPT_NUMBER;
    do {
        rc = FMD_WriteSector(sector, pBuffer, pInfo, 1);
    } while (!rc && --retry > 0);
    return rc;
}

BOOL FlashEraseBlock(BLOCK_ID block)
{
    BOOL rc;
    UINT32 retry = NAND_ATTEMPT_NUMBER;
    do {
        rc = FMD_EraseBlock(block);
    } while (!rc && --retry > 0);
    return rc;
}

//------------------------------------------------------------------------------
//
//FlashReadNANDFlash(UINT32 startBlock, UINT32 endBlock) read NAND Flash from startBlock to endBlock 
//to test if it can be read successfully
//
BOOL FlashReadNANDFlash(UINT32 startBlock, UINT32 endBlock)
{
    BOOL rc = FALSE;
    FlashInfo flashInfo;    
    BLOCK_ID block;
    UINT32  sectorsPerBlock;
    SECTOR_ADDR sector;
    SectorInfo sectorInfo;

    EdbgOutputDebugString("+FlashReadNANDFlash\r\n");

    if (!FMD_GetInfo(&flashInfo))
    {
        EdbgOutputDebugString("ERROR: FlashReadNANDFlash: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
        if (!FetchRelocTable())   
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
            return rc;
        }
    }

    sectorsPerBlock = flashInfo.wSectorsPerBlock;
    block = startBlock;
    
    while (block <= endBlock)
    {
        //no need to read the relocation table, FMD_ReadSector will do the mapping operation
        sector = block * sectorsPerBlock;
        while(sector<(block+1) * sectorsPerBlock)
        {
        if (!FlashReadSector(sector, tempBuffer, &sectorInfo))
        {
            EdbgOutputDebugString("ERROR: FlashRead: Failed read sector %d from flash\r\n", sector);
            return rc;
        }
            sector++;
        }
            EdbgOutputDebugString(".", block);
        block++;
    }

    EdbgOutputDebugString("-FlashReadNANDFlash successfully\r\n");
    return TRUE;
}

//------------------------------------------------------------------------------
//
//FlashFormatNANDFlash(UINT32 startBlock, UINT32 endBlock) format the blocks 
// from startBlock to endBlock
//When it fail to erase block, the FMD_EraseBlock will update the relocation table
//
BOOL FlashFormatNANDFlash(UINT32 startBlock, UINT32 endBlock)
{
    BOOL rc = FALSE;
    FlashInfo flashInfo;        
    BLOCK_ID block = startBlock;

    EdbgOutputDebugString("+FlashFormatNANDFlash: startBlock = %d, endBlock = %d\r\n", startBlock, endBlock);

    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: FlashFormatNANDFlash: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
        if (!FetchRelocTable())   
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
            return rc;
        }
    }

    do {
        EdbgOutputDebugString(".");
        if (!FlashEraseBlock(block))
        {
            EdbgOutputDebugString("ERROR: FlashFormatNANDFlash: Erase block %d failed!\r\n", block);
            return rc;
        }
    } while (++block <= endBlock);

    EdbgOutputDebugString("\r\n-FlashFormatNANDFlash: startBlock = %d, endBlock = %d block = %d\r\n", startBlock, endBlock, block);

    return TRUE;
}


//------------------------------------------------------------------------------
//
//  Function:  ReadBlock read one block
//  

static BOOL ReadBlock(BLOCK_ID block, UINT8 *pDataBuffer, FlashInfo * pFlashInfo)
{
    BOOL rc = FALSE;
    UINT32 count;
    UINT32 sectorSize, sectorsPerBlock;
    SectorInfo sectorInfo;
    SECTOR_ADDR sector;

    // Get flash info
    sectorSize = pFlashInfo->wDataBytesPerSector;
    sectorsPerBlock = pFlashInfo->wSectorsPerBlock;

    // Read data from block
    if (pDataBuffer != NULL)
    {
        sector = block * sectorsPerBlock;
        for(count = 0; count < sectorsPerBlock; count++)
        {
            //EdbgOutputDebugString("FMD_ReadSector: sector=%d\r\n", sector);
            if (!FlashReadSector(sector, pDataBuffer, &sectorInfo))
            {
                // When block read fail, there isn't much we can do more
                EdbgOutputDebugString("ERROR: ReadBlock: Failed read sector %d from flash\r\n",  sector);
                return rc;
            }
            // Move to next sector
            sector++;
            pDataBuffer += sectorSize;
        }
    }
    else
        return rc;

    return TRUE;
}


//------------------------------------------------------------------------------
//
//  Function:  WriteBlock
//  
// BOOL fSectorInfo:
//  TRUE means the sector info data include in the data buffer
//  FALSE means the sector info data don't include in the data buffer

static UINT8 *WriteBlock(BLOCK_ID block, UINT8 *pDataBuffer, FlashInfo * pFlashInfo, BOOL fSectorInfo)
{   
    UINT32 count;
    UINT32 sectorSize, sectorsPerBlock;
    SectorInfo sectorInfo;
    SECTOR_ADDR sector;
    UINT32 nextSectorSize;


    // Get flash info
    sectorSize = pFlashInfo->wDataBytesPerSector;
    sectorsPerBlock = pFlashInfo->wSectorsPerBlock;

    // Erase block
    if (!FlashEraseBlock(block))
    {
        EdbgOutputDebugString("ERROR: WriteBlock: Erase block %d failed!\r\n", block);
        //    EdbgOutputDebugString("WARNING: Would just try to write the block directly\r\n");
        return NULL;
    }

    // Read data from block
    if (pDataBuffer != NULL)
    {
        sector = block * sectorsPerBlock;
        for(count = 0; count < sectorsPerBlock; count++)
        {
            unsigned int i;
            BOOL allones = TRUE;
            //EdbgOutputDebugString("-");
            // Prepare sector info
            if (fSectorInfo)
            {
                memcpy(&sectorInfo, pDataBuffer+sectorSize,sizeof(SectorInfo));
                // In case the BIN/DIO image accidentally sets bBadBlock to anything other than 0xFF...
                // (this will turn a good block bad!)
                if (sectorInfo.bBadBlock != 0xFF)
                {
                    EdbgOutputDebugString("WARNING: Bad block byte is set (%B) on sector %d of block %d! Resetting to 0xFF.\r\n", sectorInfo.bBadBlock, count, block);
                    sectorInfo.bBadBlock = 0xFF; 
                }
                nextSectorSize = sectorSize+sizeof(SectorInfo);
            }
            else
            {               
                memset(&sectorInfo, 0xFF, sizeof(sectorInfo));
                sectorInfo.bOEMReserved &= ~(OEM_BLOCK_READONLY|OEM_BLOCK_RESERVED);
                sectorInfo.dwReserved1 = 0;
                sectorInfo.wReserved2 = 0;
                nextSectorSize = sectorSize;
            }

            for(i=0;i<nextSectorSize;i++)
            {
                if ((*(pDataBuffer+i))!=0xff)
                {
                    allones = FALSE;
                    break;
                }
            }

            if (!allones) 
            {
                // Mark the Sector as Write in Progress
                sectorInfo.wReserved2 &= ~SECTOR_WRITE_IN_PROGRESS;
                if (!FlashWriteSector(sector, NULL, &sectorInfo))
                {
                    // Write failed, mark block as bad
                    FMD_SetBlockStatus(block, BLOCK_STATUS_BAD);
                    EdbgOutputDebugString("ERROR: WriteBlock: Failed write sector %d to flash\r\n", sector);
                    return NULL;
                }
                // Mark the Sector as Completed if success
                sectorInfo.wReserved2 &= ~SECTOR_WRITE_COMPLETED;

                if (!FlashWriteSector(sector, pDataBuffer, &sectorInfo))
                {
                    EdbgOutputDebugString("ERROR: WriteBlock: Failed write sector %d to flash\r\n", sector);
                    return NULL;
                }
            }
            else
            {
                //EdbgOutputDebugString("INFO: WriteBlock: all-one write sector %d to flash\r\n", sector);
                //dumpData((char *)(pDataBuffer+sectorSize), 8, 0);
                // Do nothing here. Don't program this page. Just skip it.
            }

            //Move to next sector
            sector++;
            pDataBuffer += nextSectorSize;
        }
    }
    else
        return NULL;
    
    return pDataBuffer;
}

BOOL FlashReadNAND(UINT32 startBlock, UINT8 *pDataBuffer, UINT32 size)
{
    BOOL rc = FALSE;
    FlashInfo flashInfo;
    SECTOR_ADDR sector;
    BLOCK_ID block = startBlock;
    SectorInfo sectorInfo;

    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: FlashReadNAND: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
        if (!FetchRelocTable())   
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
            return rc;
        }
    }

    if (!pDataBuffer)
    {
        EdbgOutputDebugString("ERROR: no buffer for configuration data!\r\n");
        return rc;
    }
    else
    {
        sector = block * flashInfo.wSectorsPerBlock;

        if (!FlashReadSector(sector, tempBuffer, &sectorInfo))
        {
            // When block read fail, there isn't much we can do more
            EdbgOutputDebugString("ERROR: FlashRead: Failed read sector %d from flash\r\n", sector);
            return rc;
        }

        memcpy(pDataBuffer, tempBuffer, (size>LB_BYTES_PER_PAGE)?LB_BYTES_PER_PAGE:size); //todo: dan to fix the small block issue.
    }
    return (TRUE);
}

BOOL FlashWriteNAND(UINT32 startBlock, UINT8 *pDataBuffer, UINT32 size)
{
    BOOL rc = FALSE;
    RELOC_TABLE_ERRORS_T status;
    FlashInfo flashInfo;
    UINT32 blockSize;
    BLOCK_ID block = startBlock; 
    BLOCK_ID nextBlock, newBlock;
    UINT32 count, numBlocksProgram, i;
    Rel_T tableEntry;

    //EdbgOutputDebugString("+FlashWriteNAND: startBlock = %d size = %d.\r\n", startBlock, size);

    if (pDataBuffer == NULL)
    {
        EdbgOutputDebugString("ERROR: no buffer for configuration data!\r\n");
        return rc; 
    }    
    
    // Get flash info
    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: FlashWrite: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
    if (!FetchRelocTable())     
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
        return rc;
        }
    }

    blockSize = flashInfo.dwBytesPerBlock;

    numBlocksProgram = size / blockSize;
    if ((numBlocksProgram == 0) || (size%blockSize != 0)) numBlocksProgram++;
    

    EdbgOutputDebugString("FlashWrite: blockSize = 0x%x\r\n", blockSize);
    EdbgOutputDebugString("FlashWrite: start writing at block %d\r\n", block);
    EdbgOutputDebugString("FlashWrite: numBlocksProgram: %d\r\n", numBlocksProgram);

    // Write image down to flash
    for (count = 0; count < numBlocksProgram; count++)
    {
        EdbgOutputDebugString(".");
        // Scan for bad block in bad block table
        nextBlock = block;
        status = ScanBadBlockTable(&g_RelocTable, block, &block);
        if (status == NO_TABLE_FOUND) 
            return rc;
        
        i = 3;
        do 
        {
            //EdbgOutputDebugString("FlashWriteNAND: pData: %x\r\n", pDataBuffer);

            if (WriteBlock(block, pDataBuffer, &flashInfo, FALSE) == NULL)
            {   
                // Let us try three times
                
                // Relocate block
                newBlock = GetNextReservedBlock(&g_RelocTable);

                // Update bad block table
                tableEntry.To = (UINT16)newBlock;
                tableEntry.From = (UINT16)block;
                if (!UpdateBadBlockTable(&g_RelocTable, &tableEntry))
                    return rc;
                i--;
            }
            else
                break;
        } while (i > 0);

        if (i == 0)
        {
            EdbgOutputDebugString("ERROR: FlashWrite: failed!\r\n");
            return rc;
        }

        if (nextBlock != block)
            block = nextBlock;
        
        block++;
        pDataBuffer += blockSize;
    }

    EdbgOutputDebugString("\r\nFlashWrite: Image written\r\n");
   
    return TRUE;
}


BOOL ReservedSpareArea(BLOCK_ID start, int NoOfBlocks)
{
    volatile UINT8 *pDataBuffer = (UINT8 *)OS_RAM_IMAGE_START;                                      
    UINT8 *pNextData;
    FlashInfo flashInfo;
    PVOID hFMD = NULL;
    UINT32 blockSize;
    BLOCK_ID nextBlock;
    RELOC_TABLE_ERRORS_T status;
    BLOCK_ID block = start;
    UINT16 count;
    DWORD oemReserved = (BLOCK_STATUS_READONLY|BLOCK_STATUS_RESERVED);

    //EdbgOutputDebugString("+ReservedSpareArea: startBlockID = %d NoOfBlocks = %d\r\n", start, NoOfBlocks);

    // Get flash info
    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: LoadImage: FMD_GetInfo call failed!\r\n");
        return FALSE;
    }
                    
    blockSize = flashInfo.dwBytesPerBlock;

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
    if (!FetchRelocTable())     
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
        return FALSE;
        }
    }

    nextBlock = start;
    // Read Blocks
    for (count = 0; count < NoOfBlocks; count++)
    {
        // Scan for bad block
        status = ScanBadBlockTable(&g_RelocTable, nextBlock, &block);
        if (status == NO_TABLE_FOUND) 
        {
            EdbgOutputDebugString("No table found\r\n");
            return FALSE;
        }

        //EdbgOutputDebugString(".");

        if(!ReadBlock(block, (UINT8 *)pDataBuffer, &flashInfo))
            block = nextBlock;
        if(!ReadBlock(block, (UINT8 *)pDataBuffer, &flashInfo))
        {
            EdbgOutputDebugString("Failure to read block %d\r\n", block);
            return FALSE;
        }

        //EdbgOutputDebugString("Write block %d\r\n", block);
        pNextData = WriteBlock(block, (UINT8 *)pDataBuffer, &flashInfo, FALSE);
        if (pNextData == NULL)
        {
            EdbgOutputDebugString("Failure to write block %d\r\n", block);
            return FALSE;
        }
        pDataBuffer = pNextData;
        nextBlock++;
    }

    //EdbgOutputDebugString("-ReservedSpareArea \r\n");
    return TRUE;
}



static BLOCK_ID GetNextReservedBlock(Reloc_T * pTableAddress)
{
    UINT16 numReloc;
    Rel_T * pRelo;
    UINT16 lastReservedBlock = (LB_NAND_RESERVED_BLOCK_MAX > SB_NAND_RESERVED_BLOCK_MAX) ? (LB_NAND_RESERVED_BLOCK_MAX) : (SB_NAND_RESERVED_BLOCK_MAX);
    PVOID hFMD=NULL;
    FlashInfo flashInfo;

    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: FetchRelocTable: FMD_GetInfo call failed!\r\n");
        return lastReservedBlock;
    }

    // Get number of elements previously stored
    lastReservedBlock = (UINT16)flashInfo.dwNumBlocks;
    numReloc = pTableAddress->NumReloc;
    pRelo = pTableAddress->Relo;
    
    // Search for smallest block ID number 
    while (numReloc > 0)
    {
        lastReservedBlock = MIN(pRelo->To, lastReservedBlock);
        pRelo++;
        numReloc--;
    }

    // Select next reserved block used for block replacement
    if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
    {
        if ((lastReservedBlock > LB_NAND_RESERVED_BLOCK_MIN) && (lastReservedBlock <= LB_NAND_RESERVED_BLOCK_MAX))
        lastReservedBlock--;
        else
        {
            pRelo->To = (UINT16)LB_NAND_RESERVED_BLOCK_MAX;
            lastReservedBlock = pRelo->To;                
        }
    }
    else
    {
        if ((lastReservedBlock > SB_NAND_RESERVED_BLOCK_MIN) && (lastReservedBlock <= SB_NAND_RESERVED_BLOCK_MAX))
            lastReservedBlock--;
        else
        {
            pRelo->To = (UINT16)SB_NAND_RESERVED_BLOCK_MAX;
            lastReservedBlock = pRelo->To;                
        }
    }

    return lastReservedBlock;
}


//------------------------------------------------------------------------------
//
//  Function:  GetRelocTableAddr
//
/*
    Block 0 (A0)
    Offset: 0x0 = Binary Image entry point 
    Offset: 0x3E00 (16kB, 512/32)    = Relocation Table
    Offset: 0x7E00 (32kB, 512/64)     = Relocation Table
    Offset: 0xF800 (64kB, 2048/32)   = Relocation Table
    Offset: 0x1F800 (128kB, 2048/64) = Relocation Table

    Block 0 (A1 ONWARDS)
    Offset: 0x0 = Caddo state
    Offset: 0x4 = Binary Image entry point
    Offset: 0x3E00 (16kB, 512/32)    = Relocation Table
    Offset: 0x7E00 (32kB, 512/64)     = Relocation Table
    Offset: 0xF800 (64kB, 2048/32)   = Relocation Table
    Offset: 0x1F800 (128kB, 2048/64) = Relocation Table
*/

static UINT32 GetRelocTableOffset(PLATFORM_MODE_T mode, UINT32 blockSize)
{
    UINT32 offset=0;

    if (mode == NON_TRUSTED_MODE_A0)
    {
        switch ((blockSize)>>10)
        {   
            case BLOCK_SIZE_16KB:
                offset = 0x3E00;
                break; 
            case BLOCK_SIZE_32KB:
                offset = 0x7E00;
                break; 
            case BLOCK_SIZE_64KB:
                offset = 0xF800;
                break; 
            case BLOCK_SIZE_128KB:
                offset = 0x1F800;
                break;
            default:
                offset = 0x3E00;
        }
    }

    return offset;
}


//------------------------------------------------------------------------------
//
//  Function:  UpdateBadBlockTable
//

static BOOL UpdateBadBlockTable(Reloc_T * pTableAddress, Rel_T * pTableEntry)
{
    BOOL rc = FALSE;
    UINT32 numReloc;
    Rel_T * pRelo;
    FlashInfo flashInfo;
    UINT32 blockSize;

    UINT8 * g_tempBufferBlock0 = (UINT8*)(OALPAtoVA(IMAGE_BLOCK_ZERO_BACKUP_PA, FALSE));

    // Check if this is a correct address of bad block table
    if (pTableAddress == NULL) 
    {
           EdbgOutputDebugString("UpdateBadBlockTable with invalid Header\r\n");
           return rc;
    }
    else
    {
        if (pTableAddress->Header != (UINT16)RELOCATION_TABLE_HEADER)
        {
           EdbgOutputDebugString("UpdateBadBlockTable with invalid Header\r\n");
           return rc;
        }
    }


    // Get number of elements previously stored
    numReloc = pTableAddress->NumReloc;
    pRelo = pTableAddress->Relo;
    
    // Get pointer to the new entry in the table
    pRelo = pRelo + numReloc;
    
    // Update table
    pRelo->To = pTableEntry->To;
    pRelo->From = pTableEntry->From;
    pTableAddress->NumReloc = numReloc+1;
    
    // Get flash info
    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: UpdateBadBlockTable: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if (!ReadBlock((BLOCK_ID)RELOCATION_TABLE_BLOCK_ID, g_tempBufferBlock0, &flashInfo))
    {
        EdbgOutputDebugString ("ERROR: UpdateBadBlockTable: ReadBlock call failed!\r\n");
        return rc;
    }

    blockSize = flashInfo.dwBytesPerBlock;
    
    // Update buffer with new entries in Relocation Table
    memcpy(g_tempBufferBlock0 + GetRelocTableOffset(NON_TRUSTED_MODE_A0, blockSize),
            pTableAddress, sizeof(Reloc_T)); 

    // Store updated table into the flash
    if (WriteBlock((BLOCK_ID)RELOCATION_TABLE_BLOCK_ID, 
                    g_tempBufferBlock0, 
                    &flashInfo, FALSE) == NULL)
    {
        EdbgOutputDebugString("ERROR: UpdateBadBlockTable: WriteBlock call failed!\r\n");
        return rc;
    }
    
    EdbgOutputDebugString("UpdateBadBlockTable success\r\n");
    return TRUE;
}


BOOL CreateRelocTable(BLOCK_ID writeBlock, BLOCK_ID lastProtectBlock, BOOL fEmpty)
{
    FlashInfo flashInfo;
    BOOL rc = FALSE;
    BLOCK_ID block = 1;
    BLOCK_ID nextBlock, newBlock;
    RELOC_TABLE_ERRORS_T status;    
    int h, i;   
    Rel_T tableEntry;   
    PVOID hFMD = NULL;
    UINT8 *pDataBuffer = (UINT8 *)OS_RAM_IMAGE_START;
    UINT8 *pZeroBuffer;
    UINT32 blockSize;

    memset(&g_RelocTable, 0xFF, sizeof(Reloc_T));   

    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: FetchRelocTable: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    EdbgOutputDebugString("Total blocks = %d\r\n", flashInfo.dwNumBlocks);

    g_RelocTable.Header = RELOCATION_TABLE_HEADER;
    g_RelocTable.NumReloc = 0x00;   

    // Need to write the first one ...
    if (!ReadBlock((BLOCK_ID)writeBlock, pDataBuffer, &flashInfo))
    {
        EdbgOutputDebugString ("ERROR: UpdateBadBlockTable: ReadBlock call failed!\r\n");
        return rc;
    }

    blockSize = flashInfo.dwBytesPerBlock;
    
    // Update buffer with new entries in Relocation Table
    memcpy(pDataBuffer + GetRelocTableOffset(NON_TRUSTED_MODE_A0, blockSize),
            &g_RelocTable, sizeof(Reloc_T)); 

    // Store updated table into the flash
    if (WriteBlock((BLOCK_ID)writeBlock, 
                    pDataBuffer, 
                    &flashInfo, FALSE) == NULL)
    {
        EdbgOutputDebugString("ERROR: UpdateBadBlockTable: WriteBlock call failed!\r\n");
        return rc;
    }

    if (fEmpty == TRUE)
        return TRUE;

    pZeroBuffer = pDataBuffer + blockSize;
    memset(pZeroBuffer, 0x0, blockSize);    

    nextBlock = 1;
    //while (block != flashInfo.dwNumBlocks)
    while (nextBlock != flashInfo.dwNumBlocks)
    {
        BOOL fStatus = TRUE;
        EdbgOutputDebugString(".");
        // Scan for bad block in bad block table        
        status = ScanBadBlockTable(&g_RelocTable, nextBlock, &block);
        if (status == NO_TABLE_FOUND) 
        {
            EdbgOutputDebugString("Reloc Table not found\r\n");
            return rc;
        }

        h = 5;
        do {
            i = 3;
            do 
            {           
                fStatus = ReadBlock(block, pDataBuffer, &flashInfo);                
                    if (fStatus == TRUE)
                    {
                        if (WriteBlock(block, pDataBuffer, &flashInfo, FALSE) == NULL)
                            fStatus = FALSE;
                        else 
                            if (nextBlock>lastProtectBlock)
                                if (WriteBlock(block, pZeroBuffer, &flashInfo, FALSE) == NULL)
                                    fStatus = FALSE;
                                else
                                    break;
                            else
                                break;  
                }
                EdbgOutputDebugString("CreateReloTable:Scan(Read/Write)Block %d failed\r\n", block);
                i--;                
            } while (i > 0);
    
            if (fStatus == FALSE)
            {
                newBlock = GetNextReservedBlock(&g_RelocTable);

                // Update bad block table
                tableEntry.To = (UINT16)newBlock;
                tableEntry.From = (UINT16)nextBlock;        
                EdbgOutputDebugString("Update Bad block: %d => %d\r\n", tableEntry.From, tableEntry.To);
                block = newBlock;
                if (!UpdateBadBlockTable(&g_RelocTable, &tableEntry)) 
                {
                    EdbgOutputDebugString("ERROR: Update Bad Block Table: failed!\r\n");
                    return rc;
                }
            } else {
                // Success in read/write test. Jump out of h loop and proceed next block.
                break;
            }
            h--;
        } while (h > 0);
        
        if (fStatus == FALSE)
        {
            EdbgOutputDebugString("ERROR: Unable to find a usable relo block!\r\n");
            return rc;
        }
        
        nextBlock++;
    }

    EdbgOutputDebugString("CreateRelocTable completed\r\n");

    return TRUE;
}


BOOL ReadRelocTable(BLOCK_ID block)
{
    BOOL rc = FALSE;
    UINT8 *pDataBuffer = (UINT8 *)OS_RAM_IMAGE_START;
    FlashInfo flashInfo;
    UINT32 blockSize;
    Reloc_T *pTmp;
    int i;

    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: FetchRelocTable: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if (!ReadBlock(block, pDataBuffer, &flashInfo))
    {
        EdbgOutputDebugString ("ERROR: UpdateBadBlockTable: ReadBlock call failed!\r\n");
        return rc;
    }

    blockSize = flashInfo.dwBytesPerBlock;

    pTmp = (Reloc_T *)(pDataBuffer+GetRelocTableOffset(NON_TRUSTED_MODE_A0, blockSize));
    EdbgOutputDebugString("Table Address = 0x%x\r\n", GetRelocTableOffset(NON_TRUSTED_MODE_A0, blockSize));
    EdbgOutputDebugString("Header = 0x%x\r\n", pTmp->Header);
    EdbgOutputDebugString("NumReloc = 0x%x\r\n", pTmp->NumReloc);

    if (pTmp->Header == RELOCATION_TABLE_HEADER)
    {
        for (i = 0; i < pTmp->NumReloc; i++)
            EdbgOutputDebugString("%d: From:0x%x, To:0x%x\r\n", i, pTmp->Relo[i].From,pTmp->Relo[i].To);
    }
    else
    {
        EdbgOutputDebugString("ERROR IN READING RELOCATION TABLE\r\n");
        dumpData((UINT8 *)pTmp, flashInfo.wDataBytesPerSector, FALSE);
    }
    
        
    return TRUE;
}


BOOL LoadImageFromFlash(BLOCK_ID imageStartBlock, UINT8 *pImageBufferRam, UINT16 numBlocks)
{
    BOOL rc = FALSE;
    UINT16 count;
    UINT16 sectorsPerBlock;
    UINT32 blockSize; 
    FlashInfo flashInfo;
//    SectorInfo sectorInfo;
//    SECTOR_ADDR sector;
    RELOC_TABLE_ERRORS_T status;
    BLOCK_ID block = imageStartBlock;
    BLOCK_ID nextBlock;
    PVOID hFMD = NULL;
    //UINT8 *pTmp = pImageBufferRam;

    EdbgOutputDebugString("LoadImageFromFlash: Block: 0x%X (%d), pImageBufferRAM: 0x%X, numBlocks 0x%H (%d)\r\n",
        imageStartBlock, imageStartBlock, pImageBufferRam, numBlocks, numBlocks);

    if (pImageBufferRam == NULL) return rc; 

    // Get flash info
    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: LoadImage: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
        if (!FetchRelocTable())   
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
            return rc;
        }
    }
    
    blockSize = flashInfo.dwBytesPerBlock;
    sectorsPerBlock = flashInfo.wSectorsPerBlock;
    EdbgOutputDebugString("blockSize = 0x%X (%d), sectorsPerBlock = 0x%X (%d)\r\n", blockSize, blockSize, sectorsPerBlock, sectorsPerBlock);

    EdbgOutputDebugString("numBlocks = 0x%x\r\n", numBlocks);
    nextBlock = block;
    EdbgOutputDebugString("\r\n");
    for (count = 0; count < numBlocks; count++)
    {
        EdbgOutputDebugString(".");
        // Scan for bad block
        status = ScanBadBlockTable(&g_RelocTable, nextBlock, &block);
        if (status == NO_TABLE_FOUND) 
        {
            EdbgOutputDebugString("No table found\r\n");
            return rc;
        }
        if (!ReadBlock(block, pImageBufferRam, &flashInfo)) 
        {
            EdbgOutputDebugString("ReadBlock failed\r\n");
            return rc;
        }
        pImageBufferRam += blockSize;
        nextBlock++;
    }

    //DumpBuffer(pTmp, blockSize*numBlocks);
    EdbgOutputDebugString("\r\n");
    return TRUE;
}


void DumpBootLoaderImage(void)
{
    UINT8 *pDataBuffer = (UINT8 *)OS_RAM_IMAGE_START;
    FlashInfo flashInfo;
    BOOL status = FALSE;


    if (FMD_GetInfo(&flashInfo) == FALSE) 
    {
        EdbgOutputDebugString("ERROR: FetchRelocTable: FMD_GetInfo call failed!\r\n");
        return;
    }

    if(flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE)
        status = LoadImageFromFlash(LB_FLASH_BLOCK_EBOOT_START, pDataBuffer, LB_FLASH_BLOCK_EBOOT_LENGTH);
    else
        status = LoadImageFromFlash(FLASH_BLOCK_EBOOT_START, pDataBuffer, FLASH_BLOCK_EBOOT_LENGTH);
    
    if (status)
    {
        int i = 0;
        UINT32 *pTmp = (UINT32 *)pDataBuffer;
        for (i = 0; i < 256; i++)
            EdbgOutputDebugString("[0x%x]:0x%x\r\n", pTmp+i, *(pTmp+i));
    }
}





static BOOL StartFlashWriteBINDIO(DWORD dwStartAddr, DWORD dwLength)
{
    HANDLE hFMD = NULL; 
    FlashInfo flashInfo;    
    UINT32 blockSize;
    UINT32 numBlocksProgram;

    // Get & save flash info
    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: FlashWrite: FMD_GetInfo call failed!\r\n");
        return FALSE;
    }

    blockSize = flashInfo.dwBytesPerBlock;
    g_EBootContext.dwNumBlocksWritten = 0;
    numBlocksProgram = dwLength / blockSize;
    if ((numBlocksProgram == 0) || (dwLength%blockSize != 0)) numBlocksProgram++;

    if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
    {
        g_EBootContext.dwBaseAddr = (LB_FLASH_BLOCK_OS_START * blockSize);
        g_EBootContext.dwStartBlock = LB_FLASH_BLOCK_OS_START;
        FlashFormatNANDFlash(LB_FLASH_BLOCK_OS_START, flashInfo.dwNumBlocks-1-RESERVED_BLOCK_NUM);
    }
    else
    {
        g_EBootContext.dwBaseAddr = (FLASH_BLOCK_OS_START * blockSize);
        g_EBootContext.dwStartBlock = FLASH_BLOCK_OS_START;
        FlashFormatNANDFlash(FLASH_BLOCK_OS_START, flashInfo.dwNumBlocks-1-RESERVED_BLOCK_NUM);
    }

    EdbgOutputDebugString("\r\nStartFlashWriteBINDIO: start:0x%x, len: 0x%x\r\n", dwStartAddr, dwLength);

    return TRUE;
}

static BOOL ContinueFlashWriteBINDIO(PEBOOT_CONTEXT pEBootContext)
{
    BOOL rc = FALSE;
    RELOC_TABLE_ERRORS_T status;
    FlashInfo flashInfo;    
    UINT32 blockSize;
    BLOCK_ID phyBlock; 
    BLOCK_ID virBlock, reloBlock;
    UINT32 count, numBlocksProgram, i, j;
    Rel_T tableEntry;
    UINT8 *pNextData;
    UINT32 startBlock;
    UINT8 *pDataBuffer = pEBootContext->pbData;
    UINT32 size = pEBootContext->cbData;
    UINT32 address;
    UINT32 dataSize;

    if (pDataBuffer == NULL) return rc; 
    
    // Get flash info
    if (!FMD_GetInfo(&flashInfo)) 
    {
        EdbgOutputDebugString("ERROR: FlashWrite: FMD_GetInfo call failed!\r\n");
        return rc;
    }

    if ( g_RelocTable.Header != RELOCATION_TABLE_HEADER)
    {   
        if (!FetchRelocTable())   
        {
            EdbgOutputDebugString("ERROR: FetchRelocTable() call failed!\r\n");
            return rc;
        }
    }

    blockSize = flashInfo.dwBytesPerBlock;
    address = (pEBootContext->dwOffsetAddr-pEBootContext->dwBaseAddr);
    dataSize = blockSize + (flashInfo.wSectorsPerBlock * 8); // 8 = sizeof(SectorInfo)
    if (flashInfo.wDataBytesPerSector == LB_BYTES_PER_PAGE )
    {
        startBlock = LB_FLASH_BLOCK_OS_START+(address/dataSize);
    }
    else
    {
        startBlock = FLASH_BLOCK_OS_START+(address/dataSize);
    }

    virBlock = startBlock;

    numBlocksProgram = size / dataSize;
    if ((numBlocksProgram == 0) || (size%dataSize!=0)) {
        EdbgOutputDebugString("FlashWriteBINDIO: not making sense size=%d\r\n", size);
        numBlocksProgram++;
    }

    //EdbgOutputDebugString("CFWB: Size = %d at block:%d numBlocksProgram:%d\r\n", dataSize, virBlock, numBlocksProgram);

    // Write image down to flash
    for (count = 0; count < numBlocksProgram; count++)
    {
        EdbgOutputDebugString(".");
        // Scan for bad block in bad block table
        status = ScanBadBlockTable(&g_RelocTable, virBlock, &phyBlock);
        if (status == NO_TABLE_FOUND) 
            return rc;
        if (phyBlock!=virBlock)
                EdbgOutputDebugString("INFO: ContinueFlashWrite: Relo Block %d to %d.\r\n", virBlock, phyBlock);
        
        i = NAND_ATTEMPT_NUMBER;
        do 
        {
            //EdbgOutputDebugString("FlashWriteNAND: pData: %x\r\n", pDataBuffer);
            // Let us try three times.
            j = NAND_ATTEMPT_NUMBER;
            do
            {
                pNextData = WriteBlock(phyBlock, pDataBuffer, &flashInfo, TRUE);
                j--;
            } while ((!pNextData)&&(j>0));
            
            if (pNextData != NULL)
                break;
            else
            {   
                EdbgOutputDebugString("WARNING: FlashWrite: New bad block found at physical block %d!\r\n", phyBlock);
                
                // Relocate block
                reloBlock = GetNextReservedBlock(&g_RelocTable);

                // Update bad block table
                tableEntry.To = (UINT16)reloBlock;
                tableEntry.From = (UINT16)virBlock;
                if (!UpdateBadBlockTable(&g_RelocTable, &tableEntry))
                {
                    EdbgOutputDebugString("ERROR: FlashWrite: Unable to update relo table!\r\n");
                    return rc;
                }
                i--;
                phyBlock = reloBlock;
            }
        } while (i > 0);

        if (i == 0)
        {
            EdbgOutputDebugString("ERROR: FlashWrite: Still failed after 3 relo attempts!\r\n");
            return rc;
        }

        virBlock++;
        pDataBuffer = pNextData;
    }

    //EdbgOutputDebugString("\r\nFlashWriteBINDIO: Image written\r\n");

    g_EBootContext.dwNumBlocksWritten += numBlocksProgram;
    //UpdateImageAddr(startBlock, numBlocksProgram);
    
    return TRUE;
}

static void DumpBuffer(UINT8 *pImageBufferRam, UINT32 size)
{
    unsigned int i = 0;
    EdbgOutputDebugString("\r\n");

    EdbgOutputDebugString("pImageBufferRam Address at 0x%x\r\n", pImageBufferRam);
    for (i = 0; i < size; i++)
        EdbgOutputDebugString("[%x]:\t%x\r\n", (pImageBufferRam+i), *(pImageBufferRam+i));
}

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Description:
    Dumps data to the debug display formated in hex and ascii for easy viewing.
    Used for debug output only.  It is not compiled into the retail version.
Arguments:
    data            Buffer of data to be displayed
    num_bytes        Number of bytes to display
    offset          Beginning offset to be displayed before each line
Return Value:
    None
---------------------------------------------------------------------------*/
void dumpData( unsigned char * data,    WORD num_bytes, WORD offset )
{
        unsigned int i,j,l;
    unsigned char tmp_str[100];
    unsigned char tmp_str1[10];

    for (i = 0; i < num_bytes; i += 16)
    {
        unsigned int n ;
        tmp_str[0]='\0';

        n = i+offset ;
        for (j=0; j<4; j++) {
            l=n%16;
            if (l>=10)
                tmp_str[3-j]=(unsigned char)('A'+l-10);
            else
                tmp_str[3-j]=(unsigned char)(l+'0');
            n >>= 4 ;
        }
        tmp_str[4]='\0';
        strcat ( (char *)tmp_str, ": ");
        /*
          Output the hex bytes
        */
        for (j = i; j < (i+16); j++)
        {
            int m ;
            if (j < num_bytes)
            {
                m=((unsigned int)((unsigned char)*(data+j)))/16 ;
                if (m>=10)
                    tmp_str1[0]='A'+(unsigned char)m-10;
                else
                    tmp_str1[0]=(unsigned char)m+'0';
                m=((unsigned int)((unsigned char)*(data+j)))%16 ;
                if (m>=10)
                    tmp_str1[1]='A'+(unsigned char)m-10;
                else
                    tmp_str1[1]=(unsigned char)m+'0';
                tmp_str1[2]='\0';
                strcat ((char *)tmp_str, (char *)tmp_str1);
                strcat ((char *)tmp_str, " ");
            }
            else
            {
                strcat((char *)tmp_str,"   ");
            }
        }

        strcat((char *)tmp_str, "  ");
        l=(unsigned short)strlen((char *)tmp_str);

        /*
         * Output the ASCII bytes
        */
        for (j = i; j < (i+16); j++)
        {
            if (j < num_bytes)
            {
                char c = *(data+j);

                if (c < ' ' || c > 'z')
                {
                    c = '.';
                }
                tmp_str[l++]=c;
            }
            else
            {
                tmp_str[l++]=' ';
            }
        }
        tmp_str[l++]='\r';
            tmp_str[l++]='\n';
        tmp_str[l++]='\0';
            OALMSG(TRUE, (L"%S", (tmp_str)));
    }
}



